﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;

namespace EpiMapit
{
    public class KmlGenerator
    {

        private GeoCodingResources geoCodingResources;
        private CaseSetAccumulator accumulator;
        private RingGenerator ring;
        private CaseSetGroupTransform transform;
        private double lodMin;
        private double lodMax;
        private int lodSteps;
        private double lodBase;
        private bool usePins;

        public KmlGenerator(RingGenerator ringGenerator, CaseSetGroupTransform transform, double lodMin, double lodMax, int lodSteps, bool usePins)
        {
            this.ring = ringGenerator;
            this.transform = transform;
            this.lodMin = lodMin;
            this.lodMax = lodMax;
            this.lodSteps = lodSteps;
            this.lodBase = Math.Pow(10, Math.Log(lodMax / lodMin, 10) / (lodSteps - 1));
            this.usePins = usePins;
            this.geoCodingResources = new GeoCodingResources();
            this.accumulator = new CaseSetAccumulator();
        }

        public XDocument Generate(CaseSetGroup allGroups)
        {
            var allGroupsOriginal = allGroups.Clone();

            allGroups = accumulator.Transform(allGroups);

            if (transform != null)
            {
                allGroups = transform(allGroups);
            }

            var description = GetDescription(allGroupsOriginal);

            var caseSetKmlGenerator = new CaseSetKmlGenerator(ring, description, usePins);
            var caseSetGroups = allGroups.GroupBy(g => g.Date);

            int caseSetGroupCounter = 0;
            foreach (var caseSetGrouping in caseSetGroups)
            {
                var caseSetGroup = new CaseSetGroup();
                caseSetGroup.AddRange(caseSetGrouping);

                var lod = new LevelOfDetail(caseSetGroup);
                int total = caseSetGroup.Count;


                for (int i = 0; i < lodSteps; i++)
                {       
                    float cluster = (float)(lodMin * Math.Pow(lodBase, i));
                    Console.WriteLine("Cluster: " + cluster);
                    lod.Cluster(cluster);

                    caseSetKmlGenerator.Add(caseSetGroup, GetVisibility(i, lodSteps),i);
                }

                caseSetGroupCounter++;
            }

            return caseSetKmlGenerator.Document;
        }

        private Visibility GetVisibility(int i, int levels)
        {
            if (i == 0)
                if (levels == 1)
                    return Visibility.Always;
                else
                    return Visibility.FirstLevel;
            else if (i == levels - 1)
                return Visibility.LastLevel;
            else
                return Visibility.InBetweenLevel;
        }

        private Description GetDescription(CaseSetGroup allGroupsOriginal)
        {
            return (caseSet, caseSetGroup) =>
            {
                StringBuilder sb = new StringBuilder();

                sb.Append(String.Format("Total cases: {0}<br/>", caseSet.TotalSize()));

                string pieChart = PieUrlGenerator.GetPieChartUrl(300, 150, caseSet);
                string pieChartString = CreateChartString("Types of cases:", pieChart);

                var matches = allGroupsOriginal.Where(c => caseSet.RegionCodes.Any(r => c.RegionCodes.Contains(r)));
                int numberOfDates;
                string curveChart = CurveUrlGenerator.GetCurveChartUrl(300, 150, matches, null, out numberOfDates);

                if (numberOfDates == 1)
                {
                    sb.Append(String.Format("Date: {0}<br/>", caseSet.Date.ToShortDateString()));
                }

                sb.Append(pieChartString);
                
                if (numberOfDates >= 2)
                {
                    string curveChartString = CreateChartString("Epi Curves", curveChart);

                    sb.Append("<br/>");
                    sb.Append(curveChartString);
                }

                return sb.ToString();
            };
        }

        private string CreateChartString(string title, string chartUrl)
        {
            string ret = string.Format(@"<b>{0}:</b><img src=""{1}"" width=""300"" height=""150""></img>", title, chartUrl);
            return ret;
        }

    }

    public delegate CaseSetGroup CaseSetGroupTransform(CaseSetGroup group);
}
